/*
 * Copyright ©2021 su binglun(9085309@qq.com). All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *---------------------------------------------------------------------
 *-                                                                   -
 *-           Website : http://phoenix.sunny360.cn/                   -
 *-                                                                   -
 *---------------------------------------------------------------------
 */
package cn.sunny360.core.common;

import cn.sunny360.core.utils.Container;
import cn.sunny360.messsage.MsgResult;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.ext.web.RoutingContext;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class HttpHandlerMapping {
    public static Logger logger = LoggerFactory.getLogger(HttpHandlerMapping.class);
    private Map<String, HandlerInfo> handlerMap;
    public static HttpHandlerMapping newInstance() {
        return new HttpHandlerMapping();
    }
    public HttpHandlerMapping() {
        handlerMap = new HashMap<>();
    }

    public HttpHandlerMapping addHandler(String method, String path, String handler) {
        handlerMap.put(method + "#" + path, new HandlerInfo(method, path, handler));
        return this;
    }

    public List<HandlerInfo> getHandlerList() {
        return handlerMap.entrySet()
                .stream()
                .map(Map.Entry::getValue)
                .collect(Collectors.toList());
    }

    public static class HandlerInfo {
        private String method;
        private String path;
        private HandlerDecorator handler;

        public HandlerInfo(String method, String path, String handler) {
            this.method = method;
            this.path = path;
            this.handler = new HandlerDecorator(handler);
        }

        public String getMethod() {
            return method;
        }

        public void setMethod(String method) {
            this.method = method;
        }

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public HandlerDecorator getHandler() {

            return handler;
        }

        public void setHandler(String handler) {
            this.handler = new HandlerDecorator(handler);;
        }
    }

    public static class HandlerDecorator {
        private Object object;
        private Class clazz;
        private Method method;

        /**
         * handler装饰器构造方法。
         *
         * @param handler 字符串类型的构造参数。其中包括类名及方法名称
         *                例如:cn.sunny360.core.handler.http.UserHandler#getOnlineList(io.vertx.ext.web.RoutingContext)
         *                或者：cn.sunny360.core.handler.http.UserHandler#getOnlineList
         *                方法固定参数就是io.vertx.ext.web.RoutingContext
         */
        public HandlerDecorator(String handler) {
            try {
                if (handler.indexOf("(") > 0) {
                    handler = handler.substring(0, handler.indexOf("("));
                }
                String[] strings = handler.split("#");
                clazz = Class.forName(strings[0]);
                object = Container.getComponent(clazz);
                if (object == null) {
                    object = clazz.newInstance();
                    Container.putComponent(clazz, object);
                }
                method = clazz.getMethod(strings[1], RoutingContext.class);
            } catch (Exception e) {
                logger.error("handler初始化失败", e);
            }
        }

        public void handle(RoutingContext context) {
            try {
                method.invoke(object, context);
            } catch (Exception e) {
                logger.error("handler["+method+"]执行失败", e);
                badResult(context, "handler执行失败", e);
            }
        }

        protected void badResult(RoutingContext context, String msg, Throwable t) {
            MsgResult result = MsgResult.newInstance()
                    .setStatus(500)
                    .setMsg(msg)
                    .setData(printStackTrace(t));
            context.response()
                    .setStatusCode(200)
                    .putHeader("content-type", "application/octet-stream;charset=utf-8")
                    .end(Buffer.buffer(result.toByteArray()));
        }

        private String printStackTrace(Throwable t) {
            String data = "";
            if (t != null) {
                StringWriter writer = new StringWriter();
                PrintWriter printWriter = new PrintWriter(writer);
                t.printStackTrace(printWriter);
                data = writer.toString();
            }
            return data;
        }
    }

}
